package ejb.Servlet;
import static java.lang.System.out;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSSerializer;

import ejb.Entity.TweetWithGeo;
import ejb.Service.SearchSessionBean;

/**
 * @author Wei Chen, Yu Qiao, Igor. Class SearchServlet.
 */
@WebServlet("/SearchServlet")
public class SearchServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	/**
	 * Field searchSessionBean is an enterprise java session bean object.
	 * 
	 */
	@EJB
	private SearchSessionBean searchSessionBean;

	/**
	 * @see HttpServlet#HttpServlet()
	 */
	public SearchServlet() {
		super();
	}
	/**
	 * The doGet method for handling an HTTP get request. 
	 */
	protected final void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		final long startTime = System.nanoTime();
		// code

		List<TweetWithGeo> result = null;
		Integer count = null;
		String[] locationArray = null;
		String[] keywordArray = null;
		final int option = 0;
		if (request.getParameter("locations") != null) {
			locationArray = (request.getParameter("locations")).toUpperCase()
					.split(",");
		}
		if (request.getParameter("keywords") != null) {
			keywordArray = (request.getParameter("keywords")).toUpperCase()
					.split(",");
		}

		final PrintWriter writer = response.getWriter();
		response.setCharacterEncoding("UTF-8");
		response.setContentType("text/xml");

		String xmlString = "";
		try {
			final DocumentBuilderFactory builderFactory = DocumentBuilderFactory
					.newInstance();
			final DocumentBuilder docBuilder = builderFactory
					.newDocumentBuilder();
			final Document doc = docBuilder.newDocument();
			if (request.getParameter("option") != null) {
				count = this.searchSessionBean.getTweetFromH2(keywordArray,
						locationArray, option);
				xmlString = new SearchServlet().createXmlTree1(doc, count);
				out.print(xmlString);
			} else {
				result = this.searchSessionBean.getTweetFromH2(keywordArray,
						locationArray);
				xmlString = new SearchServlet().createXmlTree2(doc, result);
			}
		} catch (Exception e) {
			System.out.println(e);
		}
		writer.write(xmlString);
		writer.close();
		final long endTime = System.nanoTime();
		final float scale = 1000000000F;
		out.println("Took " + (endTime - startTime) / scale + " s");
	}
	
	/**
	 * Create a string representation of the XML with the number of returned tweets.
	 * @param doc the XML document object to be appended.
	 * @param count the number of tweets retrieved.  
	 * @return a string representation of the doc.
	 */
	public final String createXmlTree1(Document doc, Integer count)
			throws Exception {
		// This method creates an element node; Element implements Node
		final Element root = doc.createElement("root");
		// adding a node after the last child node of the specified node.
		// out.println("1");
		doc.appendChild(root);
		final Element newNode = doc.createElement("count");
		newNode.appendChild(doc.createTextNode(Integer.toString(count)));
		root.appendChild(newNode);
		return getStringFromDoc(doc);
	}

	/**
	 * Create a string representation of the XML with the number of returned tweets.
	 * @param doc the XML document object to be appended.
	 * @param result a list of TWeetWithGeo objects.  
	 * @return a string representation of the doc.
	 */
	public final String createXmlTree2(Document doc, List<TweetWithGeo> result)
			throws Exception {
		// This method creates an element node; Element implements Node
		// out.println("the size is "+result.size());
		final Element root = doc.createElement("root");
		// adding a node after the last child node of the specified node.
		// out.println("1");
		doc.appendChild(root);
		if (result == null) {
			return getStringFromDoc(doc);
		}
		// out.println("2");
		final XPath xpath = XPathFactory.newInstance().newXPath();

		XPathExpression exp;
		Object exprResult;
		NodeList nodeList;

		for (int i = 0; i < result.size(); i++) {
			final TweetWithGeo a = result.get(i);
			// out.println(a.getStname());
			exp = xpath.compile("//state[@name=\"" + a.getStname() + "\"]");
			exprResult = exp.evaluate(doc, XPathConstants.NODESET);
			nodeList = (NodeList) exprResult;
			if (nodeList.getLength() > 0) { // if state exists
				final Node curState = nodeList.item(0);
				exp = xpath.compile("//location[@geoId=\"" + a.getGeo_id()
						+ "\"]");
				exprResult = exp.evaluate(curState, XPathConstants.NODESET);
				nodeList = (NodeList) exprResult;
				if (nodeList.getLength() > 0) { // if location exists
					final Node curLocation = nodeList.item(0);
					final Element newTweet = doc.createElement("tweet");
					// 1
					newTweet.appendChild(doc.createTextNode(createCDATA(a
							.getText())));
					curLocation.appendChild(newTweet);
				} else {
					final Element newTweet = doc.createElement("tweet");
					// 2
					newTweet.appendChild(doc.createTextNode(createCDATA(a
							.getText())));

					final Element newLocation = doc.createElement("location");
					newLocation.setAttribute("geoId",
							Integer.toString(a.getGeo_id()));
					newLocation.setAttribute("city", a.getCity());
					newLocation
							.setAttribute("lat", Double.toString(a.getLat()));
					newLocation
							.setAttribute("lon", Double.toString(a.getLon()));

					newLocation.appendChild(newTweet);
					curState.appendChild(newLocation);
				}
			} else {
				final Element newState = doc.createElement("state");
				newState.setAttribute("name", a.getStname());

				final Element newTweet = doc.createElement("tweet");
				// 3
				newTweet.appendChild(doc.createTextNode(createCDATA(a.getText())));

				final Element newLocation = doc.createElement("location");
				newLocation.setAttribute("geoId",
						Integer.toString(a.getGeo_id()));
				newLocation.setAttribute("city", a.getCity());
				newLocation.setAttribute("lat", Double.toString(a.getLat()));
				newLocation.setAttribute("lon", Double.toString(a.getLon()));

				newLocation.appendChild(newTweet);
				newState.appendChild(newLocation);
				root.appendChild(newState);
			}
		}
		return getStringFromDoc(doc);
	}

	public static String createCDATA(String s) {
		// return "<![CDATA["+s+"]]>";
		return s;
	}

	/**
	 * Write a Doc object into a string.
	 * @return a string representation of the doc object.
	 */
	public final String getStringFromDoc(Document doc) {
		final DOMImplementationLS domImplementation = (DOMImplementationLS) doc
				.getImplementation();
		final LSSerializer lsSerializer = domImplementation.createLSSerializer();
		return lsSerializer.writeToString(doc);
	}

}
